﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics;
using MP3ToAudioBookWithChapters.Library.Properties;

namespace MP3ToAudioBookWithChapters.Library
{
    public class AudiobookManager
    {
        public bool IsWorking
        {
            get;
            private set;
        }

        public string AudiobookFileName
        {
            get;
            set;
        }

        public List<AudiobookChapter> Chapters
        {
            get;
            private set;
        }

        public string ChapterPath
        {
            get;
            set;
        }

        public delegate void ProgressUpdatedDelegate(int percentComplete);
        public delegate void LogUpdatedFormatDelegate(string logText, params string[] args);
        public delegate void GenerateCancelledDelegate();

        public event ProgressUpdatedDelegate ProgressUpdated;
        public event LogUpdatedFormatDelegate LogUpdated;
        public event GenerateCancelledDelegate GenerateCancelled;

        private Process process;

        public AudiobookManager(string path)
        {
            process = null;

            ChapterPath = path;
            AudiobookFileName = Path.Combine(path, Path.GetFileName(path)) + ".m4b";
            Chapters = new List<AudiobookChapter>();

            ScanForChapters();
        }

        private void ScanForChapters()
        {
            Chapters.Clear();
            var files = Directory.GetFiles(ChapterPath, "*.mp3");

            foreach (var file in files)
            {
                var chapter = new AudiobookChapter(file);
                Chapters.Add(chapter);
            }
        }

        public void Generate()
        {
            if (IsWorking)
                throw new MP3ToAudioBookWithChaptersException("Cannot generate whilst generating");

            IsWorking = true;
            var tempFiles = new Queue<string>();

            try
            {
                var sourceTags = TagLib.File.Create(Chapters[0].FileName).Tag;

                // now convert it to m4a
                var inputFiles = string.Empty;
                var i = 0;

                foreach (var chapter in Chapters)
                {
                    var wavFile = Path.Combine(Path.GetDirectoryName(chapter.FileName), string.Format("{0:000}.wav", i+1));
                    inputFiles += string.Format("-if \"{0}\" ", Path.GetFileName(wavFile));
                                        
                    NotifyLogUpdated("Converting {0} to WAV...", Path.GetFileName(chapter.FileName));
                    tempFiles.Enqueue(wavFile);
                    if (File.Exists(wavFile))
                    {
                        //File.Delete(wavFile);
                        continue;
                    }
                
                    var argsLame = string.Format("--decode \"{0}\" \"{1}\"", chapter.FileName, wavFile);
                    RunProcess(Settings.Default.LamePath, argsLame);
                    NotifyProgressUpdated(++i * 50 / Chapters.Count);
                }

                if (File.Exists(AudiobookFileName))
                    File.Delete(AudiobookFileName);

                NotifyLogUpdated("Combining and converting WAV files to MP4...");

                var previousFolder = Environment.CurrentDirectory;
                Environment.CurrentDirectory = Path.GetDirectoryName(AudiobookFileName);
                var argsConvert = string.Format("{0} -of \"{1}\"", inputFiles, AudiobookFileName);
                RunProcess(Settings.Default.NeroEncoderPath, argsConvert);
                NotifyProgressUpdated(75);
                Environment.CurrentDirectory = previousFolder;

                if (File.Exists(AudiobookFileName))
                {
                    NotifyLogUpdated("Marking QuickTime chapters...");
                    var argsChaps = string.Format("-c -Q \"{0}\"", AudiobookFileName);
                    RunProcess(Settings.Default.Mp4ChapsPath, argsChaps);
                    NotifyProgressUpdated(80);

                    NotifyLogUpdated("Updating tag information...");
                    var file = TagLib.File.Create(AudiobookFileName);
                    file.Tag.AlbumArtists = sourceTags.AlbumArtists;
                    file.Tag.Album = sourceTags.Album;
                    file.Tag.Genres = sourceTags.Genres;
                    file.Tag.Performers = sourceTags.Performers;
                    file.Tag.Title = sourceTags.Album;
                    file.Tag.Track = 1;
                    file.Tag.Year = sourceTags.Year;
                    file.Save();
                    NotifyProgressUpdated(85);
                }
                else
                {
                    NotifyLogUpdated("Cannot file file {0}!", AudiobookFileName);
                    return;
                }
            }
            catch (Exception ex)
            {
                if (process != null)
                    process.Kill();

                if (GenerateCancelled != null)
                    GenerateCancelled();
                
                NotifyLogUpdated("Generate Cancelled! {0}", ex.Message);
            }

            while (tempFiles.Count > 0)
            {
                var file = tempFiles.Dequeue();
                NotifyLogUpdated("Cleaning up temporary file {0}", Path.GetFileName(file));
                try
                {
                    if (File.Exists(file))
                        File.Delete(file);
                }
                catch (Exception ex)
                {
                    NotifyLogUpdated("Unable to cleanup temp file. {0}", ex.Message);
                }
            }
            
            NotifyLogUpdated("All done!");
            NotifyProgressUpdated(100);

            IsWorking = false;
        }

        //private void NotifyLogUpdated(string message)
        //{
        //    NotifyLogUpdatedFormat(message);
        //}

        private void NotifyLogUpdated(string message, params string[] args)
        {
            if (LogUpdated != null)
                LogUpdated(message, args);
        }

        private void NotifyProgressUpdated(int percentComplete)
        {
            if (ProgressUpdated != null)
                ProgressUpdated(percentComplete);
        }

        private void RunProcess(string exePath, string argsChaps)
        {
            var si = new ProcessStartInfo(exePath, argsChaps);
            si.CreateNoWindow = true;
            si.WindowStyle = ProcessWindowStyle.Hidden;
            process = Process.Start(si);
            process.WaitForExit();
            if (process.HasExited && process.ExitCode != 0)
                throw new MP3ToAudioBookWithChaptersException(string.Format("Unable to run process. {0}", process.StandardError.ReadToEnd())); 
            process = null;
        }
    }
}

